变换
仿射变换
- UIView的
transform
属性,用于视图在二维空间做旋转,缩放和平移 - 仿射变换的定义,是指无论变换矩阵用什么值,图层中平行的两条线在变换之后仍然保持平行
- Core Graphics提供的实例化仿射变换的方法
- CGAffineTransformMakeRotation(CGFloat angle) // 旋转
- CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) // 缩放
- CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) // 平移
- CALayer也有一个
transform
属性,但它的类型是CATransform3D,而不是CGAffineTransform - Core Graphics提供了在之前变换的基础上继续变换的方法
- CGAffineTransformRotate(CGAffineTransform t, CGFloat angle)
- CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy)
- CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
- 以及一个单位矩阵常量CGAffineTransformIdentity
- 我们可以利用这些函数,在必要的时候组合一个更加复杂的变化,这样的实现方式,是优雅的
1 | - (void)viewDidLoad { |
3D变换
- 和CGAffineTransform类似,CATransform3D也是一个矩阵,但是和2x3的矩阵不同,CATransform3D是一个可以在3维空间内做变换的4x4的矩阵,我们要关注zPosition
3D变换的方法
- CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
- CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz)
CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
与仿射变化的旋转不同,CATransform3DMakeRotation是绕某一个(x,y,z)轴进行旋转
透视效果
- 三维空间中,当物体原理我们时,会看起来比较小,远处的物体和近处的物体存在不同的缩放比例
- 为了实现透视效果,我们需要修改
m34
值,m34
的默认值是0,我们可以通过设置m34
为(-1.0 / d)来实现透视效果,d代表了想象中视角相机和屏幕之间的距离,以像素为单位,通常500~1000就可以了。代码如下:
1 | - (void)viewDidLoad { |
灭点
- 当在透视角度绘图的时候,远离相机视角的物体将会变小变远,当远离到一个极限距离,它们可能就缩成了一个点,于是所有的物体最后都汇聚消失在同一个点。这个点叫灭点
- Core Animation定义的这个点位于变换前
anchorPoint
的位置 - 当一个图层有多个3D变换的子图层,我们要想让整个视图绘制的更有3D效果,应该首先把它们都放置于屏幕中央,然后通过平移来把它移动到指定位置(而不是直接改变它的position),这样所有的3D图层都共享一个灭点
sublayerTransform属性
- 如果图层有多个子图层都要做3D变换,那就要分别对
m34
进行设置,CALayer有一个属性叫做sublayerTransform
,这个变换会作用于所有子图层
1 | - (void)viewDidLoad { |
图层的背面
- 图层是双面绘制的,反面是一个镜像图片,这并不是一个很好的特性,会给用户造成困扰,同时也可能造成系统资源的浪费,试想我们不想看到背面,那为什么还要浪费GPU绘制它们?
doubleSided
属性,用来控制图层背面是否绘制,默认为YES
专用图层
CALayer类具有一些非常有用的绘图和动画功能。但Core Animation不仅作用于图片和颜色,CALayer拓展了其他一些专用于某种功能的子类,以增强Core Animation的绘图能力。这里学习总结了几个常用的专用图层,其他的仅作了解
CAShapeLayer - 形状图层
- 阴影可以使用CGPath来构建轮廓,图层也可以用这种方式构建
- CAShapeLayer是一个通过矢量绘图的图层子类
- 比较推荐的方式是使用UIBezierPath类来帮助创建图层,这样我们不用考虑人工释放CGPath资源
1 | // 利用CAShapeLayer新建图层绘制一个火柴人 |
- CAShapeLayer还有一个比较常用的用法,是绘制矩形的指定圆角,UIBezierPath有一个自动绘制圆角矩形的构造方法
- 目前常用的做法是在UIView的分类添加以下方法,创建图层蒙版
1 | - (void)addRoundedCorners:(UIRectCorner)corners radius:(CGFloat)radii{ |
这么做有两个问题,一是当我们同时需要创建指定圆角和阴影时,不可避免要添加一个新的Container图层;二是动态修改
frame.size
时,蒙版路径并不会更新(准确说pathRect
就是初始bounds
值)
在书中提出【我们可以把CAShapeLayer作为视图的宿主图层,而不是添加一个子视图】。经过实验,一个可行的方案是,我们重写UIView的
+layerClass
方法,返回指定图层类CAShapeLayer.class
,并在图层布局的时候指定path,注意,如果要指定CAShapeLayer背景图层着色,要使用fillColor
属性而不能直接写backgroundColor
1 | + (Class)layerClass{ |
CAGradientLayer - 渐变图层
- CAGradientLayer是用来生成两种或更多颜色平滑渐变的。通常我们绘制渐变图,会用Core Graphics的
CGContextDrawLinearGradient()
方法生成一张图片,但相较于这种方式,CAGradientLayer使用了硬件加速使整个流程更加效率,并且代码上更加简洁优雅 - 创建一个简单的渐变图层,此处注意接收渐变色彩数组的类型是CGColorRef,为了保证编译正常需要bridge转换
1 | - (void)viewDidLoad { |
CAReplicatorLayer - 重复图层
CAReplicatorLayer
的目的是为了高效生成许多相似的图层CAReplicatorLayer
的几个常用属性instanceCount
创建多少个指定图层的拷贝instanceDelay
两次拷贝间的延迟instanceTransform
基于上次拷贝的仿射变换
一般和
CAAnimationGroup
、CAShapeLayer
组合使用书中并没有举很多实用性的例子,但其实这个图层使用还是挺广泛的,例如雷达,波纹,加载球球,咻一咻效果等。总的来说,这个图层的使用需要一些想象力
CAEmitterLayer - 粒子图层
CAEmitterLayer
是一个高性能的粒子引擎,被用来创建实时例子动画如:烟雾,火,雨等等这些效果。实际应用比较广泛,比如微信的红包雨,直播间的小❤❤这是一个很实用的图层,之后我会专门写一篇博客来讲讲他的使用